package com.redis.sdk.cachecore;

import com.redis.sdk.Spel;
import com.github.benmanes.caffeine.cache.Cache;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Aspect
@Component
@Order(OrderSort.LOCAL_CACHE_ORDER)
@ConditionalOnProperty(prefix = "cache", name = "openLocalCache", havingValue = "true")
@Slf4j
public class CacheLocalAspect {

    @Autowired
    private Cache<String, Object> cache;

    /**
     * 拦截指定注解
     */
    @Pointcut("@annotation(com.redis.sdk.cachecore.CacheLocal)")
    public void cacheLocal() {
    }

    /**
     * 拦截指定注解
     */
    @Pointcut("@annotation(com.redis.sdk.cachecore.CacheLocalUpdate)")
    public void cacheLocalUpdate() {
    }

    /**
     * 拦截指定注解
     */
    @Pointcut("@annotation(com.redis.sdk.cachecore.CacheLocalDelete)")
    public void cacheLocalDelete() {
    }

    @Around("cacheLocal()")
    public Object cacheLocal(ProceedingJoinPoint pjp) throws Throwable {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();

        CacheLocal annotation = method.getAnnotation(CacheLocal.class);
        String key = Spel.parseKey(annotation.value(), method, pjp.getArgs());
        key = annotation.prefix() + key;
        log.info("开始从本地缓存尝试获取数据, key: {}", key);
        Object cacheValue = cache.getIfPresent(key);
        if (null != cacheValue) {
            log.info("从本地缓存获取到数据, key: {}", key);
            return cacheValue;
        }
        log.info("未从本地缓存获取到数据, key: {}", key);

        Object value = pjp.proceed();
        if (null != value) {
            log.info("开始本地缓存, key: {}", key);
            cache.put(key, value);
        }
        return value;
    }

    @Around("cacheLocalUpdate()")
    public Object cacheLocalUpdate(ProceedingJoinPoint pjp) throws Throwable {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();

        CacheLocalUpdate annotation = method.getAnnotation(CacheLocalUpdate.class);
        String key = Spel.parseKey(annotation.value(), method, pjp.getArgs());
        key = annotation.prefix() + key;
        Object value = pjp.proceed();
        if (null != value) {
            log.info("开始更新本地缓存, key: {}", key);
            cache.put(key, value);
        }
        return value;
    }

    @Around("cacheLocalDelete()")
    public Object cacheLocalDelete(ProceedingJoinPoint pjp) throws Throwable {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();

        CacheLocalDelete annotation = method.getAnnotation(CacheLocalDelete.class);
        String key = Spel.parseKey(annotation.value(), method, pjp.getArgs());
        key = annotation.prefix() + key;
        Object value = pjp.proceed();
        log.info("开始移除本地缓存, key: {}", key);
        cache.invalidate(key);
        return value;
    }

}
